{ "cells": [ { "cell_type": "markdown", "id": "c80cd461", "metadata": {}, "source": [ "(analysis-framework-tutorial)=\n", "# Tutorial 3. Building custom analyses - the data analysis framework\n", "\n", "```{seealso}\n", "\n", "The complete source code of this tutorial can be found in\n", "\n", "{nb-download}`Tutorial 3. Building custom analyses - the data analysis framework.ipynb`\n", "\n", "```\n", "\n", "Quantify provides an analysis framework in the form of a {class}`~quantify_core.analysis.base_analysis.BaseAnalysis` class and several subclasses for simple cases (e.g., {class}`~quantify_core.analysis.base_analysis.BasicAnalysis`, {class}`~quantify_core.analysis.base_analysis.Basic2DAnalysis`, {class}`~quantify_core.analysis.spectroscopy_analysis.ResonatorSpectroscopyAnalysis`). The framework provides a structured, yet flexible, flow of the analysis steps. We encourage all users to adopt the framework by sub-classing the {class}`~quantify_core.analysis.base_analysis.BaseAnalysis`.\n", "\n", "To give insight into the concepts and ideas behind the analysis framework, we first write analysis scripts to *\"manually\"* analyze the data as if we had a new type of experiment in our hands.\n", "Next, we encapsulate these steps into reusable functions packing everything together into a simple python class.\n", "\n", "We conclude by showing how the same class is implemented much more easily by extending the {class}`~quantify_core.analysis.base_analysis.BaseAnalysis` and making use of the quantify framework." ] }, { "cell_type": "code", "execution_count": null, "id": "114e888a", "metadata": { "mystnb": { "code_prompt_show": "Imports and auxiliary utilities" }, "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "import json\n", "import logging\n", "from pathlib import Path\n", "from typing import Tuple\n", "\n", "import lmfit\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import xarray as xr\n", "\n", "import quantify_core.visualization.pyqt_plotmon as pqm\n", "from quantify_core.analysis.cosine_analysis import CosineAnalysis\n", "from quantify_core.analysis.fitting_models import CosineModel, cos_func\n", "from quantify_core.data.handling import (\n", " default_datadir,\n", " get_latest_tuid,\n", " load_dataset,\n", " locate_experiment_container,\n", " set_datadir,\n", ")\n", "from quantify_core.measurement import MeasurementControl\n", "from quantify_core.utilities.examples_support import mk_cosine_instrument\n", "from quantify_core.utilities.inspect_utils import display_source_code\n", "from quantify_core.visualization.SI_utilities import set_xlabel, set_ylabel" ] }, { "cell_type": "markdown", "id": "97036a87", "metadata": {}, "source": [ "Before instantiating any instruments or starting a measurement we change the\n", "directory in which the experiments are saved using the\n", "{meth}`~quantify_core.data.handling.set_datadir`\n", "\\[{meth}`~quantify_core.data.handling.get_datadir`\\] functions.\n", "\n", "----------------------------------------------------------------------------------------\n", "\n", "⚠️ **Warning!**\n", "\n", "We recommend always setting the directory at the start of the python kernel and stick\n", "to a single common data directory for all notebooks/experiments within your\n", "measurement setup/PC.\n", "\n", "The cell below sets a default data directory (`~/quantify-data` on Linux/macOS or\n", "`$env:USERPROFILE\\\\quantify-data` on Windows) for tutorial purposes. Change it to your\n", "desired data directory. The utilities to find/search/extract data only work if\n", "all the experiment containers are located within the same directory.\n", "\n", "----------------------------------------------------------------------------------------" ] }, { "cell_type": "code", "execution_count": null, "id": "efe3fa65", "metadata": {}, "outputs": [], "source": [ "set_datadir(default_datadir()) # change me!" ] }, { "cell_type": "markdown", "id": "6795b2b8", "metadata": {}, "source": [ "## Run an experiment\n", "\n", "We mock an experiment in order to generate a toy dataset to use in this tutorial." ] }, { "cell_type": "code", "execution_count": null, "id": "881bb888", "metadata": { "mystnb": { "code_prompt_show": "Source code of a mock instrument" }, "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "display_source_code(mk_cosine_instrument)" ] }, { "cell_type": "code", "execution_count": null, "id": "f58b3e02", "metadata": { "mystnb": { "remove-output": true } }, "outputs": [], "source": [ "meas_ctrl = MeasurementControl(\"meas_ctrl\")\n", "plotmon = pqm.PlotMonitor_pyqt(\"plotmon\")\n", "meas_ctrl.instr_plotmon(plotmon.name)\n", "pars = mk_cosine_instrument()\n", "\n", "meas_ctrl.settables(pars.t)\n", "meas_ctrl.setpoints(np.linspace(0, 2, 30))\n", "meas_ctrl.gettables(pars.sig)\n", "dataset = meas_ctrl.run(\"Cosine experiment\")" ] }, { "cell_type": "code", "execution_count": null, "id": "0e3dbd26", "metadata": {}, "outputs": [], "source": [ "plotmon.main_QtPlot" ] }, { "cell_type": "markdown", "id": "b2e180c6", "metadata": {}, "source": [ "## Manual analysis steps\n", "\n", "### Loading the data\n", "\n", "The {class}`~xarray.Dataset` contains all the information required to perform a basic analysis of the experiment.\n", "We can alternatively load the dataset from disk based on its {class}`~quantify_core.data.types.TUID`, a timestamp-based unique identifier. If you do not know the tuid of the experiment you can find the latest tuid containing a certain string in the experiment name using {meth}`~quantify_core.data.handling.get_latest_tuid`.\n", "See the {ref}`data-storage` documentation for more details on the folder structure and files contained in the data directory." ] }, { "cell_type": "code", "execution_count": null, "id": "6210845e", "metadata": {}, "outputs": [], "source": [ "tuid = get_latest_tuid(contains=\"Cosine experiment\")\n", "dataset = load_dataset(tuid)\n", "dataset" ] }, { "cell_type": "markdown", "id": "868ba095", "metadata": {}, "source": [ "### Performing a fit\n", "\n", "We have a sinusoidal signal in the experiment dataset, the goal is to find the underlying parameters.\n", "We extract these parameters by performing a fit to a model, a cosine function in this case.\n", "For fitting we recommend using the lmfit library. See [the lmfit documentation](https://lmfit.github.io/lmfit-py/model.html) on how to fit data to a custom model." ] }, { "cell_type": "code", "execution_count": null, "id": "e8f19380", "metadata": {}, "outputs": [], "source": [ "# create a fitting model based on a cosine function\n", "fitting_model = lmfit.Model(cos_func)\n", "\n", "# specify initial guesses for each parameter\n", "fitting_model.set_param_hint(\"amplitude\", value=0.5, min=0.1, max=2, vary=True)\n", "fitting_model.set_param_hint(\"frequency\", value=0.8, vary=True)\n", "fitting_model.set_param_hint(\"phase\", value=0)\n", "fitting_model.set_param_hint(\"offset\", value=0)\n", "params = fitting_model.make_params()\n", "\n", "# here we run the fit\n", "fit_result = fitting_model.fit(dataset.y0.values, x=dataset.x0.values, params=params)\n", "\n", "# It is possible to get a quick visualization of our fit using a build-in method of lmfit\n", "_ = fit_result.plot_fit(show_init=True)" ] }, { "cell_type": "markdown", "id": "488679bd", "metadata": {}, "source": [ "The summary of the fit result can be nicely printed in a Jupyter-like notebook:" ] }, { "cell_type": "code", "execution_count": null, "id": "e6f191c1", "metadata": {}, "outputs": [], "source": [ "fit_result" ] }, { "cell_type": "markdown", "id": "3a6641e6", "metadata": {}, "source": [ "### Analyzing the fit result and saving key quantities" ] }, { "cell_type": "code", "execution_count": null, "id": "4c8a7ea6", "metadata": {}, "outputs": [], "source": [ "quantities_of_interest = {\n", " \"amplitude\": fit_result.params[\"amplitude\"].value,\n", " \"frequency\": fit_result.params[\"frequency\"].value,\n", "}\n", "quantities_of_interest" ] }, { "cell_type": "markdown", "id": "54821380", "metadata": {}, "source": [ "Now that we have the relevant quantities, we want to store them in the same\n", "`experiment directory` where the raw dataset is stored.\n", "\n", "First, we determine the experiment directory on the file system." ] }, { "cell_type": "code", "execution_count": null, "id": "2084197a", "metadata": {}, "outputs": [], "source": [ "# the experiment folder is retrieved with a convenience function\n", "exp_folder = Path(locate_experiment_container(dataset.tuid))\n", "exp_folder" ] }, { "cell_type": "markdown", "id": "033c7543", "metadata": {}, "source": [ "Then, we save the quantities of interest to disk in the human-readable JSON format." ] }, { "cell_type": "code", "execution_count": null, "id": "57d7ca8f", "metadata": {}, "outputs": [], "source": [ "with open(exp_folder / \"quantities_of_interest.json\", \"w\", encoding=\"utf-8\") as file:\n", " json.dump(quantities_of_interest, file)" ] }, { "cell_type": "markdown", "id": "9054cdd5", "metadata": {}, "source": [ "### Plotting and saving figures\n", "\n", "We would like to save a plot of our data and the fit in our lab logbook but the figure above is not fully satisfactory: there are no units and no reference to the original dataset.\n", "\n", "Below we create our own plot for full control over the appearance and we store it on disk in the same `experiment directory`.\n", "For plotting, we use the ubiquitous matplotlib and some visualization utilities." ] }, { "cell_type": "code", "execution_count": null, "id": "81af206d", "metadata": {}, "outputs": [], "source": [ "# create matplotlib figure\n", "fig, ax = plt.subplots()\n", "\n", "# plot data\n", "dataset.y0.plot.line(ax=ax, x=\"x0\", marker=\"o\", label=\"Data\")\n", "\n", "# plot fit\n", "x_fit = np.linspace(dataset[\"x0\"][0], dataset[\"x0\"][-1], 1000)\n", "y_fit = cos_func(x=x_fit, **fit_result.best_values)\n", "ax.plot(x_fit, y_fit, label=\"Fit\")\n", "ax.legend()\n", "\n", "# set units-aware tick labels\n", "set_xlabel(dataset.x0.long_name, dataset.x0.units)\n", "set_ylabel(dataset.y0.long_name, dataset.y0.units)\n", "\n", "# add a reference to the origal dataset in the figure title\n", "fig.suptitle(f\"{dataset.attrs['name']}\\ntuid: {dataset.attrs['tuid']}\")\n", "\n", "# Save figure\n", "fig.savefig(exp_folder / \"Cosine fit.png\", dpi=300, bbox_inches=\"tight\")" ] }, { "cell_type": "markdown", "id": "ccfab7e1", "metadata": {}, "source": [ "## Reusable fitting model and analysis steps\n", "\n", "The previous steps achieve our goal, however, the code above is not easily reusable and hard to maintain or debug.\n", "We can do better than this! We can package our code in functions that perform specific tasks.\n", "In addition, we will use the objected-oriented interface of `lmfit` to further structure our code.\n", "We explore the details of the object-oriented approach later in this tutorial." ] }, { "cell_type": "code", "execution_count": null, "id": "652768c7", "metadata": {}, "outputs": [], "source": [ "class MyCosineModel(lmfit.model.Model):\n", " \"\"\"\n", " `lmfit` model with a guess for a cosine fit.\n", " \"\"\"\n", "\n", " def __init__(self, *args, **kwargs):\n", " \"\"\"Configures the constraints of the model.\"\"\"\n", " # pass in the model's equation\n", " super().__init__(cos_func, *args, **kwargs)\n", "\n", " # configure constraints that are independent from the data to be fitted\n", "\n", " self.set_param_hint(\"frequency\", min=0, vary=True) # enforce positive frequency\n", " self.set_param_hint(\"amplitude\", min=0, vary=True) # enforce positive amplitude\n", " self.set_param_hint(\"offset\", vary=True)\n", " self.set_param_hint(\n", " \"phase\", vary=True, min=-np.pi, max=np.pi\n", " ) # enforce phase range\n", "\n", " def guess(self, data, **kws) -> lmfit.parameter.Parameters:\n", " \"\"\"Guess parameters based on the data.\"\"\"\n", "\n", " self.set_param_hint(\"offset\", value=np.average(data))\n", " self.set_param_hint(\"amplitude\", value=(np.max(data) - np.min(data)) / 2)\n", " # a simple educated guess based on experiment type\n", " # a more elaborate but general approach is to use a Fourier transform\n", " self.set_param_hint(\"frequency\", value=1.2)\n", "\n", " params_ = self.make_params()\n", " return lmfit.models.update_param_vals(params_, self.prefix, **kws)" ] }, { "cell_type": "markdown", "id": "47143c62", "metadata": {}, "source": [ "Most of the code related to the fitting model is now packed in a single object, while the analysis steps are split into functions that take care of specific tasks." ] }, { "cell_type": "code", "execution_count": null, "id": "d288a58c", "metadata": {}, "outputs": [], "source": [ "def extract_data(label: str) -> xr.Dataset:\n", " \"\"\"Loads a dataset from its label.\"\"\"\n", " tuid_ = get_latest_tuid(contains=label)\n", " dataset_ = load_dataset(tuid_)\n", " return dataset_\n", "\n", "def run_fitting(dataset_: xr.Dataset) -> lmfit.model.ModelResult:\n", " \"\"\"Executes fitting.\"\"\"\n", " model = MyCosineModel() # create the fitting model\n", " params_guess = model.guess(data=dataset_.y0.values)\n", " result = model.fit(\n", " data=dataset_.y0.values, x=dataset_.x0.values, params=params_guess\n", " )\n", " return result\n", "\n", "def analyze_fit_results(fit_result_: lmfit.model.ModelResult) -> dict:\n", " \"\"\"Analyzes the fit results and saves quantities of interest.\"\"\"\n", " quantities = {\n", " \"amplitude\": fit_result_.params[\"amplitude\"].value,\n", " \"frequency\": fit_result_.params[\"frequency\"].value,\n", " }\n", " return quantities\n", "\n", "def plot_fit(\n", " fig_: matplotlib.figure.Figure,\n", " ax_: matplotlib.axes.Axes,\n", " dataset_: xr.Dataset,\n", " fit_result_: lmfit.model.ModelResult,\n", ") -> Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]:\n", " \"\"\"Plots a fit result.\"\"\"\n", " dataset_.y0.plot.line(ax=ax_, x=\"x0\", marker=\"o\", label=\"Data\") # plot data\n", "\n", " x_fit_ = np.linspace(dataset_[\"x0\"][0], dataset_[\"x0\"][-1], 1000)\n", " y_fit_ = cos_func(x=x_fit_, **fit_result_.best_values)\n", " ax_.plot(x_fit, y_fit_, label=\"Fit\") # plot fit\n", " ax_.legend()\n", "\n", " # set units-aware tick labels\n", " set_xlabel(dataset_.x0.long_name, dataset_.x0.units, ax_)\n", " set_ylabel(dataset_.y0.long_name, dataset_.y0.units, ax_)\n", "\n", " # add a reference to the original dataset_ in the figure title\n", " fig_.suptitle(f\"{dataset_.attrs['name']}\\ntuid: {dataset_.attrs['tuid']}\")\n", "\n", "def save_quantities_of_interest(tuid_: str, quantities_of_interest_: dict) -> None:\n", " \"\"\"Saves the quantities of interest to disk in JSON format.\"\"\"\n", " exp_folder_ = Path(locate_experiment_container(tuid_))\n", " # Save fit results\n", " with open(exp_folder_ / \"quantities_of_interest.json\", \"w\", encoding=\"utf-8\") as f_:\n", " json.dump(quantities_of_interest_, f_)\n", "\n", "def save_mpl_figure(tuid_: str, fig_: matplotlib.figure.Figure) -> None:\n", " \"\"\"Saves a matplotlib figure as PNG.\"\"\"\n", " exp_folder_ = Path(locate_experiment_container(tuid_))\n", " fig_.savefig(exp_folder_ / \"Cosine fit.png\", dpi=300, bbox_inches=\"tight\")\n", " plt.close(fig_)" ] }, { "cell_type": "markdown", "id": "c9d139bd", "metadata": {}, "source": [ "Now the execution of the entire analysis becomes much more readable and clean:" ] }, { "cell_type": "code", "execution_count": null, "id": "358959d4", "metadata": {}, "outputs": [], "source": [ "dataset = extract_data(label=\"Cosine experiment\")\n", "fit_result = run_fitting(dataset)\n", "quantities_of_interest = analyze_fit_results(fit_result)\n", "save_quantities_of_interest(dataset.tuid, quantities_of_interest)\n", "fig, ax = plt.subplots()\n", "plot_fit(fig_=fig, ax_=ax, dataset_=dataset, fit_result_=fit_result)\n", "save_mpl_figure(dataset.tuid, fig)" ] }, { "cell_type": "markdown", "id": "31482522", "metadata": {}, "source": [ "If we inspect the experiment directory, we will find a structure that looks like the following:\n", "\n", "```{code-block}\n", "20230125-172712-018-87b9bf-Cosine experiment/\n", "├── Cosine fit.png\n", "├── dataset.hdf5\n", "├── quantities_of_interest.json\n", "└── snapshot.json\n", "```\n", "\n", "## Creating a simple analysis class\n", "\n", "Even though we have improved code structure greatly, in order to execute the same analysis against some other dataset we would have to copy-paste a significant portion of code (the analysis steps).\n", "\n", "We tackle this by taking advantage of the Object Oriented Programming (OOP) in python.\n", "We will create a python class that serves as a structured container for data (attributes) and the methods (functions) that act on the information.\n", "\n", "Some of the advantages of OOP are:\n", "\n", "- the same class can be instantiated multiple times to act on different data while reusing the same methods;\n", "- all the methods have access to all the data (attributes) associated with a particular instance of the class;\n", "- subclasses can inherit from other classes and extend their functionalities.\n", "\n", "Let's now observe what such a class could look like.\n", "\n", "```{warning}\n", "This analysis class is intended for educational purposes only.\n", "It is not intended to be used as a template!\n", "See the end of the tutorial for the recommended usage of the analysis framework.\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "da4a3264", "metadata": {}, "outputs": [], "source": [ "class MyCosineAnalysis:\n", " \"\"\"Analysis as a class.\"\"\"\n", "\n", " def __init__(self, label: str):\n", " \"\"\"This is a special method that python calls when an instance of this class is\n", " created.\"\"\"\n", "\n", " self.label = label\n", "\n", " # objects to be filled up later when running the analysis\n", " self.tuid = None\n", " self.dataset = None\n", " self.fit_results = {}\n", " self.quantities_of_interest = {}\n", " self.figs_mpl = {}\n", " self.axs_mpl = {}\n", "\n", " # with just slight modification our functions become methods\n", " # with the advantage that we have access to all the necessary information from self\n", " def run(self):\n", " \"\"\"Execute the analysis steps.\"\"\"\n", " self.extract_data()\n", " self.run_fitting()\n", " self.analyze_fit_results()\n", " self.create_figures()\n", " self.save_quantities_of_interest()\n", " self.save_figures()\n", "\n", " def extract_data(self):\n", " \"\"\"Load data from disk.\"\"\"\n", " self.tuid = get_latest_tuid(contains=self.label)\n", " self.dataset = load_dataset(tuid)\n", "\n", " def run_fitting(self):\n", " \"\"\"Fits the model to the data.\"\"\"\n", " model = MyCosineModel()\n", " guess = model.guess(self.dataset.y0.values)\n", " result = model.fit(\n", " self.dataset.y0.values, x=self.dataset.x0.values, params=guess\n", " )\n", " self.fit_results.update({\"cosine\": result})\n", "\n", " def analyze_fit_results(self):\n", " \"\"\"Analyzes the fit results and saves quantities of interest.\"\"\"\n", " self.quantities_of_interest.update(\n", " {\n", " \"amplitude\": self.fit_results[\"cosine\"].params[\"amplitude\"].value,\n", " \"frequency\": self.fit_results[\"cosine\"].params[\"frequency\"].value,\n", " }\n", " )\n", "\n", " def save_quantities_of_interest(self):\n", " \"\"\"Save quantities of interest to disk.\"\"\"\n", " exp_folder_ = Path(locate_experiment_container(self.tuid))\n", " with open(\n", " exp_folder_ / \"quantities_of_interest.json\", \"w\", encoding=\"utf-8\"\n", " ) as file_:\n", " json.dump(self.quantities_of_interest, file_)\n", "\n", " def plot_fit(self, fig_: matplotlib.figure.Figure, ax_: matplotlib.axes.Axes):\n", " \"\"\"Plot the fit result.\"\"\"\n", "\n", " self.dataset.y0.plot.line(ax=ax_, x=\"x0\", marker=\"o\", label=\"Data\") # plot data\n", "\n", " x_fit_ = np.linspace(self.dataset[\"x0\"][0], self.dataset[\"x0\"][-1], 1000)\n", " y_fit_ = cos_func(x=x_fit_, **self.fit_results[\"cosine\"].best_values)\n", " ax_.plot(x_fit_, y_fit_, label=\"Fit\") # plot fit\n", " ax_.legend()\n", "\n", " # set units-aware tick labels\n", " set_xlabel(self.dataset.x0.long_name, self.dataset.x0.attrs[\"units\"], ax_)\n", " set_ylabel(self.dataset.y0.long_name, self.dataset.y0.attrs[\"units\"], ax_)\n", "\n", " # add a reference to the original dataset in the figure title\n", " fig_.suptitle(f\"{dataset.attrs['name']}\\ntuid: {dataset.attrs['tuid']}\")\n", "\n", " def create_figures(self):\n", " \"\"\"Create figures.\"\"\"\n", " fig_, ax_ = plt.subplots()\n", " self.plot_fit(fig_, ax_)\n", "\n", " fig_id = \"cos-data-and-fit\"\n", " self.figs_mpl.update({fig_id: fig_})\n", " # keep a reference to `ax` as well\n", " # it can be accessed later to apply modifications (e.g., in a notebook)\n", " self.axs_mpl.update({fig_id: ax_})\n", "\n", " def save_figures(self):\n", " \"\"\"Save figures to disk.\"\"\"\n", " exp_folder_ = Path(locate_experiment_container(self.tuid))\n", " for fig_name, fig_ in self.figs_mpl.items():\n", " fig_.savefig(exp_folder_ / f\"{fig_name}.png\", dpi=300, bbox_inches=\"tight\")\n", " plt.close(fig_)" ] }, { "cell_type": "markdown", "id": "b56c4016", "metadata": {}, "source": [ "Running the analysis is now as simple as:" ] }, { "cell_type": "code", "execution_count": null, "id": "ba6ee364", "metadata": {}, "outputs": [], "source": [ "a_obj = MyCosineAnalysis(label=\"Cosine experiment\")\n", "a_obj.run()\n", "a_obj.figs_mpl[\"cos-data-and-fit\"]" ] }, { "cell_type": "markdown", "id": "6b1d19bb", "metadata": {}, "source": [ "The first line will instantiate the class by calling the {code}`.__init__()` method.\n", "\n", "As expected this will save similar files into the `experiment directory`:\n", "\n", "```{code-block}\n", "20230125-172712-018-87b9bf-Cosine experiment/\n", "├── cos-data-and-fit.png\n", "├── Cosine fit.png\n", "├── dataset.hdf5\n", "├── quantities_of_interest.json\n", "└── snapshot.json\n", "```\n", "\n", "## Extending the BaseAnalysis\n", "\n", "While the above stand-alone class provides the gist of an analysis, we can do even better by defining a structured framework that all analyses need to adhere to and factoring out the pieces of code that are common to most analyses.\n", "Besides that, the overall functionality can be improved.\n", "\n", "Here is where the {class}`~quantify_core.analysis.base_analysis.BaseAnalysis` enters the scene.\n", "It allows us to focus only on the particular aspect of our custom analysis by implementing only the relevant methods. Take a look at how the above class is implemented where we are making use of the analysis framework. For completeness, a fully documented {class}`~quantify_core.analysis.fitting_models.CosineModel` which can serve as a template is shown as well." ] }, { "cell_type": "code", "execution_count": null, "id": "0909e0d6", "metadata": {}, "outputs": [], "source": [ "display_source_code(CosineModel)\n", "display_source_code(CosineAnalysis)" ] }, { "cell_type": "markdown", "id": "4c1eee01", "metadata": {}, "source": [ "Now we can simply execute it against our latest experiment as follows:" ] }, { "cell_type": "code", "execution_count": null, "id": "c030ad1e", "metadata": {}, "outputs": [], "source": [ "a_obj = CosineAnalysis(label=\"Cosine experiment\").run()\n", "a_obj.display_figs_mpl()" ] }, { "cell_type": "markdown", "id": "5f30a46e", "metadata": {}, "source": [ "Inspecting the `experiment directory` will show something like this:\n", "\n", "```{code-block}\n", "20230125-172712-018-87b9bf-Cosine experiment/\n", "├── analysis_CosineAnalysis/\n", "│ ├── dataset_processed.hdf5\n", "│ ├── figs_mpl/\n", "│ │ ├── cos_fit.png\n", "│ │ └── cos_fit.svg\n", "│ ├── fit_results/\n", "│ │ └── cosine.txt\n", "│ └── quantities_of_interest.json\n", "├── cos-data-and-fit.png\n", "├── Cosine fit.png\n", "├── dataset.hdf5\n", "├── quantities_of_interest.json\n", "└── snapshot.json\n", "```\n", "\n", "As you can conclude from the {class}`!CosineAnalysis` code, we did not implement quite a few methods in there.\n", "These are provided by the {class}`~quantify_core.analysis.base_analysis.BaseAnalysis`.\n", "To gain some insight into what exactly is being executed we can enable the logging module and use the internal logger of the analysis instance:" ] }, { "cell_type": "code", "execution_count": null, "id": "62be0929", "metadata": { "myst_nb": { "output_stderr": "show" } }, "outputs": [], "source": [ "# activate logging and set global level to show warnings only\n", "logging.basicConfig(level=logging.WARNING)\n", "\n", "# set analysis logger level to info (the logger is inherited from BaseAnalysis)\n", "a_obj.logger.setLevel(level=logging.INFO)\n", "_ = a_obj.run()" ] } ], "metadata": { "file_format": "mystnb", "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.17" } }, "nbformat": 4, "nbformat_minor": 5 }